﻿using System;
using System.Collections.Generic;
using System.Linq;
using Ant.BasicPlugIn.Data.Conventions;
using Ant.Interface.Environment.ShellBuilders;
using FluentNHibernate;
using FluentNHibernate.Automapping;
using FluentNHibernate.Automapping.Alterations;
using FluentNHibernate.Cfg;
using FluentNHibernate.Cfg.Db;
using FluentNHibernate.Conventions.Helpers;
using FluentNHibernate.Diagnostics;
using NHibernate.Cfg;

namespace Ant.BasicPlugIn.Data.Providers
{
    [Serializable]
    public abstract class AbstractDataServicesProvider : IDataServicesProvider
    {
        public abstract IPersistenceConfigurer GetPersistenceConfigurer(bool createDatabase);

        public string Parameter { get; set; }

        public Configuration BuildConfiguration(SessionFactoryParameters parameters)
        {
            var database = GetPersistenceConfigurer(parameters.CreateDatabase);
            var persistenceModel = CreatePersistenceModel(parameters.RecordDescriptors.ToList());

            return Fluently.Configure()
                .Database(database)
                .Mappings(m => m.AutoMappings.Add(persistenceModel))
                .BuildConfiguration();
        }

        public static AutoPersistenceModel CreatePersistenceModel(IEnumerable<RecordBlueprint> recordDescriptors)
        {
            if (recordDescriptors == null)
            {
                throw new ArgumentNullException("recordDescriptors");
            }

            return AutoMap.Source(new TypeSource(recordDescriptors))

                // Ensure that namespaces of types are never auto-imported, so that
                // identical type names from different namespaces can be mapped without ambiguity
                .Conventions.Setup(x => x.Add(AutoImport.Never()))
                .Conventions.Add(new RecordTableNameConvention(recordDescriptors))
                .Conventions.Add(new CacheConvention(recordDescriptors))
                .Alterations(alt =>
                                 {
                                     foreach (
                                         var recordAssembly in recordDescriptors.Select(x => x.Type.Assembly).Distinct()
                                         )
                                     {
                                         alt.Add(new AutoMappingOverrideAlteration(recordAssembly));
                                     }
                                 });
        }

        [Serializable]
        private class TypeSource : ITypeSource
        {
            private readonly IEnumerable<RecordBlueprint> _recordDescriptors;

            public TypeSource(IEnumerable<RecordBlueprint> recordDescriptors)
            {
                _recordDescriptors = recordDescriptors;
            }

            public IEnumerable<Type> GetTypes()
            {
                return _recordDescriptors.Select(descriptor => descriptor.Type);
            }

            public void LogSource(IDiagnosticLogger logger)
            {
                throw new NotImplementedException();
            }

            public string GetIdentifier()
            {
                throw new NotImplementedException();
            }
        }
    }
}